package com.exception.global.core.domain;

import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Specification 子类，用于实现条件查询类型的拼接查询处理
 * @param <T>
 */
public class SubjectSpecification<T> implements Specification<T> {

    private List<ConditionType> conditionTypeList = new ArrayList<>();

    /**
     * 相等查询条件
     * @param fieldName 字段名称
     * @param value 字段值
     */
    public void equal(String fieldName, Object value) {
        ConditionType equal = ConditionType.EQUAL;
        equal.setN(fieldName);
        equal.setO(value);
        conditionTypeList.add(equal);
    }

    /**
     * like 查询条件
     * @param fieldName 字段名称
     * @param value 字段值，仅限 String 类型，其余类型不处理
     */
    public void like(String fieldName, String value) {
        ConditionType like = ConditionType.LIKE;
        like.setN(fieldName);
        like.setO(value);
        conditionTypeList.add(like);
    }

    /**
     * in 查询条件
     * @param fieldName 字段名称
     * @param c 字段值
     */
    public void in(String fieldName, Collection c) {
        ConditionType in = ConditionType.IN;
        in.setO(c);
        in.setN(fieldName);
        conditionTypeList.add(in);
    }

    /**
     * not in 查询条件
     * @param fieldName 字段名称
     * @param c 字段值
     */
    public void notIn(String fieldName, Collection c) {
        ConditionType notIn = ConditionType.NOT_IN;
        notIn.setN(fieldName);
        notIn.setO(c);
        conditionTypeList.add(notIn);
    }

    @Override
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
        Predicate predicate = criteriaBuilder.conjunction();
        conditionTypeList.forEach(a -> predicate.getExpressions().add(a.combine(root, criteriaBuilder, a.getN(), a.getO())));
        return predicate;
    }


    static enum ConditionType {
        EQUAL {
            public Predicate combine(Root root, CriteriaBuilder cb, String n, Object o) {
                return cb.equal(root.get(n), o);
            }
        },
        LIKE {
            public Predicate combine(Root root, CriteriaBuilder cb, String n, Object o) {
                if (o instanceof String) {
                    return cb.like(root.get(n), (String) o);
                }
                return null;
            }
        },
        IN {
            public Predicate combine(Root root, CriteriaBuilder cb, String n, Object o) {
                Expression expression = root.get(n);
                return cb.in(expression.in(o));
            }
        },
        NOT_IN {
            public Predicate combine(Root root, CriteriaBuilder cb, String n, Object o) {
                Expression expression = root.get(n);
                return cb.in(expression.in(o)).not();
            }
        };

        /**
         * 字段名称
         */
        private String n;
        /**
         * 字段值
         */
        private Object o;

        /**
         * 结合条件操作
         * @param root Root
         * @param cb cb
         * @param n 查询字段名
         * @param o 查询字段值
         * @return 查询条件
         */
        abstract Predicate combine(Root root, CriteriaBuilder cb, String n, Object o);

        public String getN() {
            return n;
        }

        public void setN(String n) {
            this.n = n;
        }

        public Object getO() {
            return o;
        }

        public void setO(Object o) {
            this.o = o;
        }
    }
}
